דלג לתוכן הראשי

Custom Attribute Directive

יצירת directive מותאם מייצרת directive שמשנה את הניראות של האלמנט בו הוא נמצא.

כדי ליצור directive נריץ ב-CLI את הפקודה:

ng generate directive directiveName

בקובץ ה-directive שנוצר לנו נקבל את האלמנט שממנו הופעל ה-directive ונפעיל עליו את הפעולה.

setbackground.directive.ts
constructor(private element: ElementRef){}

ngOnInit(){
this.element.nativeElement.style.backgroundColor = "green";
}

נשתמש ב-directive שבנינו

component.html
<div class="container" setBackground>
Some Content
</div>

דוגמא

נראה דוגמא של directive שמשנה את גודל הפונט באלמנט בו הוא מופעל.

hightlight.directive.ts
constructor(private element: ElementRef){}

ngOnInit(){
this.element.nativeElement.style.fontSize = "20px";
}
component.html
<p>This is <span appHighlight>Angular</span></p>

נניח שאנחנו רוצים תגובות שונות של ה-directive הזה לאירועים שמתרחשים בעמוד.

hightlight.directive.ts
import { Directive, ElementRef, HostListener } from '@angular/core';

constructor(private element: ElementRef){}

@HostListener('mouseenter') onMouseEnter() {
this.highlight("20px");
}

@HostListener('mouseleave') onMouseLeave() {
this.highlight("16px");
}

private highlight(fontSize: string) {
this.element.nativeElement.style.fontSize = fontSize;
}

אפשר לקבל את הערך שמשתמשים בו גם כמשתנה שמגיע כ-Input בחוץ.

בקריאה ל-directive נשלח את הערך שאנחנו רוצים.

component.html
<p>This is <span appHighlight [highlightText]="'30px'">Angular9</span></p>

אפשר לכתוב את זה בדרך מקוצרת כך:

component.html
<p>This is <span [appHighlight]="'30px'">Angular9</span></p>

ולהשתמש בערך שמקבלים בצורה הזאת:

hightlight.directive.ts
import { Directive, ElementRef, HostListener } from '@angular/core';

export class HighlightDirective {
@Input('appHighlight') highlightText: string;

constructor(private element: ElementRef){}

@HostListener('mouseenter') onMouseEnter() {
this.highlight(this.highlightText);
}

@HostListener('mouseleave') onMouseLeave() {
this.highlight("16px");
}

private highlight(fontSize: string) {
this.element.nativeElement.style.fontSize = fontSize;
}
}

Render

השתמשנו ב-nativeElement כדי לקבל גישה לאלמנט ב-DOM.

setbackground.directive.ts
constructor(private element: ElementRef){}

ngOnInit(){
this.element.nativeElement.style.backgroundColor = "green";
}

הדרך המומלצת היא לא להשתמש ישירות באלמנט של ה-DOM. יש הרבה עדכונים שמתבצעים ללא צורך כשיש שינוי ישיר באלמנט DOM וכן שינוי ישיר ב-DOM עובד בדפדפן אבל לא בסביבות אחרות שבהן אין דפדפן.

מעבר לזה DOM API לא עושה סניטציה למה שנכנס אליו ולכן הקוד יותר חשוף לפגיעות.

עם render2 אנחנו יכולים לגשת ל-DOM בצורה לא ישירה.

חוץ מהפרמטר ElementRef, אנחנו מקבלים בגישה אל ה-directive פרמטר נוסף, Renderer2.

Renderer2 הוא class שמאפשר לנו לשנות את האלמנטים ב-DOM בלי לגשת אליו ישירות.

Renderer2 נותן לנו פונקציות שמאפשרות לשייך style לאלמנט HTML או להוסיף css class או לשים attribute לאלמנט.

הפונקציה setStyle מקבלת את האלמנט, את התכונה שאותה רוצים לשנות ואת הערך לתכונה.

highlight.directive.ts
constructor(private element: ElementRef, private renderer: Renderer2){}

ngOnInit(){
// Adding style
this.renderer.setStyle(this.element.nativeElement, 'backgroundColor');
// Adding class
this.renderer.setClass(this.element.nativeElement, 'container');
// Set attribute
this.renderer.setAttribute(this.element.nativeElement, 'title', 'This is example');
}

Conditional Directive

נניח שיש לנו תצוגה של פוסטים ואנחנו רוצים שרק הפוסט עם הכי הרבה לייקים יקבל את העיצוב. אנחנו רוצים שה-directive יופעל רק בתנאי מסויים.

בקומפוננטה נגדיר את התנאי שאומר לנו מתי אנחנו רוצים להפעיל את ה-directive. נקבל את הפוסט עם הכי הרבה לייקים.

app.component.ts
posts: Post[];
mostLikedPost = this.getMostLikedPost();

getMostLikedPost(){
let postsCopy = [...this.posts];
return postsCopy.sort((curr, next) => next.likes - curr.likes)[0];
}

set מאפשר לנו להשתמש ב-property כמו פונקציה. אנחנו יכולים לכתוב לוגיקה ל-property. עכשיו ה-directive יופעל רק אם התנאי מתקיים.

highlight.directive.ts
constructor(private element: ElementRef, private renderer: Renderer2){}

@Input() set appHighlight(condition: boolean){
if(condition){
this.renderer.addClass(this.element.nativeElement, 'highlight');
}
}
app.component.html
<div class="container">
<div *ngFor="let post in posts">
<div class="post-card" [appHighlight]="post.likes === mostLikedPost.likes">
<h2>{{ post.title }}</h2>
<p>{{ post.content }}</p>
<div>{{ post.likes }}</div>
</div>
</div>
</div>